package pl.edu.mimuw.mas.visualization;

import java.util.Vector;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import pl.edu.mimuw.mas.chart.Area;
import pl.edu.mimuw.mas.chart.Chart;
import pl.edu.mimuw.mas.exception.ContextUnknownException;
import pl.edu.mimuw.mas.exception.WrongAreaException;
import pl.edu.mimuw.mas.visualization.rendering.ChartRenderer;
import pl.edu.mimuw.mas.visualization.rendering.ChartRendererContext;

/**
 * Implementacja przełączania kontekstów renderowania.
 */
public abstract class AbstractVisualization {

	private Logger log = LoggerFactory.getLogger(getClass());
	
	protected Chart chart;
	protected ChartRenderer renderer;
	protected ChartRendererContext globalContext, currentContext;
	protected Vector<ChartRendererContext> localContext;
	
	protected abstract ChartRendererContext.contextType getType();
	
	public AbstractVisualization(Chart chart) {		
		this.chart = chart;
		
		this.localContext = new Vector<ChartRendererContext>();
		
		this.globalContext = new ChartRendererContext(chart, getType());
		this.currentContext = this.globalContext;
				
		this.renderer = new ChartRenderer(this.globalContext);
	}
	
	/**
	 * Ustawia nową mapę, jednocześnie ususwa konteksty starej mapy.
	 */
    public void setChart(Chart chartReplacement) {
    	chart = chartReplacement;
     	
    	localContext.clear();
    	globalContext = new ChartRendererContext(chart, getType());    	
    	renderer.setContext(globalContext);
    	
    	currentContext = globalContext;
    	    	
    	log.debug("Ustawiono nową mapę");
    }
        
    /**
     * Tworzy nowy kontekst wyświetlający podany fragment mapy.
     *  
     * @return deskryptor kontekstu
     * @throws WrongAreaException niepoprawny opis obszaru
     */
    public int createContext(Area area) throws WrongAreaException {
		int[] a = area.getArea();
		if (a[Area.X1] >= chart.getColumnsCount() || a[Area.Y1] >= chart.getRowsCount()) {
			log.debug("Niepoprawna definicja obszaru");
			throw new WrongAreaException();
		}    	
    	
    	ChartRendererContext context = new ChartRendererContext(chart, area, getType());    	
    	localContext.add(context);
    	
    	int id = localContext.size() - 1;   
    	log.debug("Kontekst " + id + " dodany");   

    	return id;
    }
    
    /**
	 * Tworzy nowy kontekst i odrazu zastępuje nim aktualny kontekst również w
	 * {@link ChartRenderer}.
	 * 
	 * @throws WrongAreaException niepoprawny opis obszaru
	 */
    public void replaceContext(Area area) throws WrongAreaException {
    	int[] a = area.getArea();
    	if (a[Area.X1] >= chart.getColumnsCount() || a[Area.Y1] >= chart.getRowsCount()) {
			log.debug("Niepoprawna definicja obszaru");
			throw new WrongAreaException();
		}    	
    	
    	currentContext = new ChartRendererContext(chart, area, getType());
    	renderer.setContext(currentContext);
    	
    	log.debug("Kontekst zastąpiony");   
    }

    /**
     * Usrawia kontekst na inny o podanym <code>id</code>.
     * 
     * @throws ContextUnknownException nieznany kontekst
     */
    public void toggleContext(int id) throws ContextUnknownException {
    	if (id < 0 || id >= localContext.size()) {
    		log.debug("Nieznany kontekst " + id);        	
    		throw new ContextUnknownException();
    	}
    	
    	ChartRendererContext nc = localContext.get(id);
    	renderer.setContext(nc);
    	
    	log.debug("Kontekst zmieniony na " + id);
    	
    	currentContext = nc;    	    	
    }
    
    /**
     * Ustawia początkowy kontekst.
     */
    public void toggleGlobalContext() {
    	renderer.setContext(globalContext);    	  	
    	currentContext = globalContext;
    	
    	log.debug("Ustawiono globalny kontekst");   
    }
    
    /**
     * Zaznacza fragment obszaru innym kolorem.
     */
    public void select(int x0, int y0, int x1, int y1) {
    	if (Area.checkArea(x0, y0, x1, y1)) {
    		currentContext.select(x0, y0, x1, y1);
    		
    		log.debug("Zaznaczono obszar ((" + x0 + ", " + y0 + ") (" + x1 + ", " + y1 + "))");
    	} else {
			log.debug("Niepoprawna definicja obszaru");
		}
    }

    /**
     * Wyłącza zaznaczanie.
     */
    public void selectNone() {
    	currentContext.selectNone();
    	
    	log.debug("Odznaczono wszystko");
    }	
}
